import { Asset, assetManager, AssetManager, AudioClip, find, instantiate, Node, Prefab, Animation, SpriteFrame, AnimationClip, Sprite } from "cc";
import { FrameCache } from "./FrameCache";
import FrameBase from "./FrameBase";
import { FrameMask } from "./FrameMask";

export type Constructor<T = unknown> = new (...args: any[]) => T;
export type AssetType<T = Asset> = Constructor<T>;


export default class CommonUtil extends FrameBase {
    private static readonly voicePath: string = "audio";
    private static readonly propPath: string = "prop";
    private static _curBundleName: string = "resources";

    public static setBundle(name: string) {
        this._curBundleName = name
    }

    public static async loadRes<T extends Asset>(path: string, type: AssetType<T>, bundleName?: string): Promise<T> {
        let bundle = await this.getBundle(this._curBundleName);
        return new Promise((resolve, reject) => {
            bundle.load(path, type, (err: Error, res: any) => {
                if (err) {
                    this.Error(`laod error: ${path}`);
                    return reject(`laod error: ${path}`);
                } else {
                    return resolve(res);
                }
            });
        })
    }

    /**
     * 加载声音
     * @param name 声音片段名
     */
    public static async loadVoice(name: string, bundleName?: string): Promise<AudioClip> {
        if (FrameCache.CacheVoice.get(name)) {
            return Promise.resolve(FrameCache.CacheVoice.get(name));
        }

        let clip = await this.loadRes(`${this.voicePath}/${name}`, AudioClip, bundleName);
        if (clip) {
            FrameCache.CacheVoice.set(name, clip);
            return Promise.resolve(clip);
        } else {
            return Promise.reject("load Fail:" + name);
        }
    }

    /**
     * 加载弹窗
     * @param name 弹窗名
     */
    public static async loadProp(name: string): Promise<Node> {
        let maskNode = await this.laodMask();
        let propPre = null;
        let propNode = null;
        if (FrameCache.CacheProp.get(name)) {
            propPre = instantiate(FrameCache.CacheProp.get(name));
        } else {
            let prefab = await this.loadRes(`${this.propPath}/${name}`, Prefab);
            FrameCache.CacheProp.set(name, prefab);
            propNode = instantiate(prefab);
            maskNode.addChild(propNode);
            maskNode.getComponent(FrameMask).loadIcon.active = false;

        }
        return Promise.resolve(propNode);
    }

    /**
     * 弹出加载界面
     * @returns 界面节点
     */
    public static async laodMask(): Promise<Node> {
        const canvas = find("Canvas");
        let maskNode = null;
        if (FrameCache.CacheProp.get("propMask")) {
            maskNode = instantiate(FrameCache.CacheProp.get("propMask"));
        } else {
            await this.loadRes(`${this.propPath}/propMask`, Prefab).then((prefab: Prefab) => {
                FrameCache.CacheProp.set("propMask", prefab);
                maskNode = instantiate(prefab);
                canvas.addChild(maskNode);
            }).catch((err) => {
                this.Error(err);
            })
        }
        return Promise.resolve(maskNode);
    }

    private static async getBundle(bundleName: string): Promise<AssetManager.Bundle> {
        if (FrameCache.CacheBundle.get(bundleName)) {
            return Promise.resolve(FrameCache.CacheBundle.get(bundleName));
        }

        return new Promise((resolve, reject) => {
            assetManager.loadBundle(bundleName, (err, bundle) => {
                if (err) {
                    this.Error("加载失败");
                    reject(null);
                } else {
                    FrameCache.CacheBundle.set(bundleName, bundle);
                    resolve(bundle);
                }
            })
        });
    }

    /** 添加按钮点击事件 */
    public static addButtonLister(n: Node, event: string, callback: Function, target: any, waitTimeSec = 0, ...args) {
        n.off(event);
        n.on(event, () => {
            if (waitTimeSec) {
                // 防止连点，冷却时间
                let clickTime = n['clickTime'] || new Date().getTime();
                let nowTime = new Date().getTime();
                let offsetTime = (nowTime - clickTime) / 1000;
                if (offsetTime && offsetTime < waitTimeSec) return;
                n.attr({ clickTime: nowTime });
            }
            //需要自定义音效的按钮，末尾加入Audio字符串
            if (n.name.indexOf('Audio') < 0) {
                // App.audio.play(1000002);
            }
            callback.call(target, n, ...args);
        })
    }

    /** 动态添加帧动画 */
    public static addSpriteAnimation(target: Node, frames: SpriteFrame[], clipName: string, wrapMode: AnimationClip.WrapMode, speed: number) {
        let animation = target.getComponent(Animation);
        if (!animation) animation = target.addComponent(Animation);
        let clip = AnimationClip.createWithSpriteFrames(frames, 60);
        clip.name = clipName;
        clip.wrapMode = wrapMode;
        clip.speed = speed;
        animation.addClip(clip);
        animation.defaultClip = clip;
        return animation
    }
}

